﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Machine.Specifications;
using MSMSpec;
using UnitTestTarget;

namespace UnitTests
{
    public static class Subjects
    {
        public const string TestExecutionHelper = "TestExecutionHelper";
    }

    public class with_results_dictionary
    {
        protected static Dictionary<string, Action> results;
        Establish context = () => results = new Dictionary<string, Action>();
    }

    [Subject(Subjects.TestExecutionHelper)]
    public class when_processing_simple_Test : with_results_dictionary
    {
        Because of = () =>
            TestExecutionHelper.Process<test_with_an_Establish_a_Because_and_several_Its_and_Cleanup>(results);

        It should_execute_all_steps_in_sequence = () =>
            test_with_an_Establish_a_Because_and_several_Its_and_Cleanup.ResultString.ShouldEqual(
                @"Establish called. Because called. It1 called. It3 called. Cleanup called. ");
    }

    [Subject(Subjects.TestExecutionHelper)]
    public class when_processing_a_class_with_a_failing_It_and_failing_It_result_is_called :with_results_dictionary
    {
        static Exception exception;

        Establish context = () =>
            TestExecutionHelper.Process<test_with_one_failing_It>(results);

        Because of = () => exception = Catch.Exception(() => results["it"]());

        It should_throw_an_Excption = () =>
            exception.ShouldNotBeNull();
        It should_throw_exception_thrown_in_the_It = () =>
            exception.ShouldBeOfType<DivideByZeroException>();

    }

    [Subject(Subjects.TestExecutionHelper)]
    public class when_processing_test_with_a_passing_It_and_result_action_is_called : with_results_dictionary
    {
        static Exception ex;

        Establish context = () => TestExecutionHelper.Process<test_with_one_passing_It>(results);

        Because of = () => ex = Catch.Exception(() => results["foo"]());

        It should_not_throw_exception = () => ex.ShouldBeNull();
    }

    [Subject(Subjects.TestExecutionHelper)]
    public class when_processing_test_with_not_implemented_It_and_result_action_is_called : with_results_dictionary
    {
        static Exception ex;

        Establish context = () => TestExecutionHelper.Process<test_with_not_implemented_It>(results);

        Because of = () => ex = Catch.Exception(() => results["bar"]());

        It should_throw_exception = () => ex.ShouldNotBeNull();
        It should_throw_Inconclusive = () => ex.ShouldBeOfType<Microsoft.VisualStudio.TestTools.UnitTesting.AssertInconclusiveException>();
    }

    [Subject(Subjects.TestExecutionHelper)]
    public class when_processing_test_with_base_test_classes : with_results_dictionary
    {
        Because of = () => TestExecutionHelper.Process<test_class_with_ancestors>(results);

        It should_execute_all_steps_in_sequence = () =>
            test_class_with_ancestors.Result.ShouldEqual(@"base_test_class establish called. derived_base_test_class establish called. test_class establish called. test_class because called. test_class it2 called. ");
    }

    [Subject(Subjects.TestExecutionHelper)]
    public class when_processing_test_with_base_test_class_where_base_Establish_throws : with_results_dictionary
    {
        static Exception ex;

        Because of = () => ex = Catch.Exception(() => TestExecutionHelper.Process<test_class_with_base_class_that_throws_in_Establish>(results));

        It should_throw_exception = () => ex.ShouldNotBeNull();
        It should_throw_exception_thrown_by_Establish = () =>
            ex.ShouldBeOfType<DivideByZeroException>();
    }

    [Subject(Subjects.TestExecutionHelper)]
    public class when_processing_test_with_ignored_Its : with_results_dictionary
    {
        Because of = () => TestExecutionHelper.Process<test_with_ignored_Its>(results);

        It should_process_everything_except_ignored_Its = () =>
            test_with_ignored_Its.Result.ShouldEqual(
                @"Establish called. Because called. It1 called. It3 called. ");
    }

    [Subject(Subjects.TestExecutionHelper)]
    public class when_processing_ignored_test : with_results_dictionary
    {
        Because of = () => TestExecutionHelper.Process<test_class_with_Ignore_attribute>(results);

        It should_not_have_done_anything = () =>
        {
            test_class_with_Ignore_attribute.Result.ShouldEqual(string.Empty);
            results.Keys.Count.ShouldEqual(0);
        };
    }

    [Subject(Subjects.TestExecutionHelper)]
    public class when_processing_test_derived_from_abstract_base : with_results_dictionary
    {
        Because of = () => TestExecutionHelper.Process<test_derived_from_abstract_base>(results);

        It should_have_carried_out_expected_actions = () =>
            test_derived_from_abstract_base.Result.ShouldEqual(
                @"Base Establish called. Test Establish called. ItBase called. It1 called. ");

        It should_allow_verification_of_base_It = () =>
        {
            var exception = Catch.Exception(() => results["itBase"]());
            exception.ShouldNotBeNull();
            exception.ShouldBeOfType<DivideByZeroException>();
        };

        It should_allow_verification_of_derived_It = () =>
        {
            var exception = Catch.Exception(() => results["it1"]());
            exception.ShouldNotBeNull();
            exception.ShouldBeOfType<EncoderFallbackException>();
        };
    }
}
